<?php

/**
 * SigmaCMS - Content Management System
 *
 * Copyright (C) 2008 Pavol Biely
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 *
 * @package    SigmaCMS
 * @author     Pavol Biely <pavolbiely@gmail.com>
 * @copyright  2008 Pavol Biely
 * @license    http://www.gnu.org/licenses/gpl.txt   GNU General Public License
 * @link       http://pabi3.com/
 * @since      0.1.0
 */

final class Admin_Backup
{
	/**
	 * Initialization
	 *
	 * @return void
	 */
	public function initialize()
	{
		// backup in XHTML format
		if (_post('backup') && _post('xhtml')) {
			Admin::header(_ADMIN_BACKUP);
			echo "<h2>"._ADMIN_BACKUP."</h2>\n";
			echo "<textarea class='backup' rows='25' cols='55'>";
			echo _hsc($this->process(true)); // XHTML
			echo "</textarea>";
			Admin::footer();
			exit;
		}

		// backup to file
		else if (_post('backup')) {
		    // create SQL backup
			$backup = $this->process(false);

			// it is possible to create GZIP compression?
			if (function_exists('gzencode') && _post('gzip')) {
				header('Content-Type:application/x-gzip; charset=utf-8');
				header('Content-Encoding:gzip');
				$compress = true;
				$filename = 'sigmacms_'.date('Ymd').".gz";
				$backup = gzencode($backup,9);
			} else {
				header('Content-Type:text/plain; charset=utf-8');
				$compress = false;
				$filename = 'sigmacms_'.date('Ymd').".sql";
			}

			// send headers
			header('Content-Disposition: attachment; filename="'.$filename.'"');
			header('Content-Length: '.strlen($backup));
			header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
			header('Pragma: no-cache');

			// print backup data
			echo $backup;

			exit;
		}

		Admin::header(_ADMIN_BACKUP);

		// restore and backup forms
		$this->restore();
		$this->backup();

		Admin::footer();
	}

	/**
	 * Database restore form
	 *
	 * @return void
	 */
	private function restore()
	{
		if (_post('restore')) {
			$file = _files('dump');

			$dump = file_get_contents($file['tmp_name']);
			$dump = explode("\n",trim($dump));

			// number of errors
			$errors  = 0;

			// temporary SQL command
			$tmp = NULL;

			// SQL commands
			$queries = array();

			// split SQL commands and save them to array
			foreach($dump as $line) {
			    // skip comments
				if (!preg_match('/^(-|#)/',$line)) {
					$tmp .= $line;
					if (preg_match('/(.*);$/',$line)) {
						$queries[] = $tmp; // save SQL command
						$tmp = NULL; 
					}
				}
			}

			// make backup
			foreach ($queries as $query) {
				if(!db::query($query)) $errors++;
				#echo _hsc($query)."<br /><br />\n"; // DEBUG
			}

			if (count($queries) > 0) {
				if ($errors > 0) {
					Actionlog::add(sprintf(_LOG_ADMIN_BACKUP_RESTORE,$errors));
					echo _ADMIN_BACKUP_RESTORE_ERROR;
				} else {
					Actionlog::add(_LOG_ADMIN_BACKUP_RESTORE);
				    echo _ADMIN_BACKUP_RESTORE_OK;
				}
			}
		}

		echo "<h2>"._ADMIN_BACKUP_RESTORE_DB."</h2>\n";

		echo "<form action='index.php?action=backup' method='post' enctype='multipart/form-data'>\n";
		echo "<fieldset>\n";

		echo "<label for='dump'>"._ADMIN_BACKUP_RESTORE_DUMP."</label>\n";
		echo "<input id='dump' name='dump' type='file' />\n";

		echo "<div class='center'>\n";
		echo "<input name='restore' type='submit' value='"._ADMIN_BACKUP_RESTORE_SUBMIT."' />\n";
		echo "</div>\n";

		echo "</fieldset>\n";
		echo "</form>\n";
	}

	/**
	 * Database backup form
	 *
	 * @return void
	 */
    private function backup()
    {
		echo "<h2>"._ADMIN_BACKUP_DB."</h2>\n";

		echo "<form action='index.php?action=backup' method='post' onsubmit='return isSelectedAll(\"tables\",\""._ADMIN_BACKUP_TABLES_SELECT."\");'>\n";
		echo "<fieldset>\n";

		echo "<label for='tables'>"._ADMIN_BACKUP_TABLES."</label>\n";
		echo "<select id='tables' name='tables[]' multiple='multiple' class='multiple'>\n";
		$sql = db::query("SHOW TABLES");
		while ($table = db::fetchArray($sql)) {
			echo "<option value='".$table[0]."'>".$table[0]."</option>\n";
		}
		db::free($sql);
		echo "</select>\n";

		echo "<div class='center'>\n";
		echo "<span onclick='return selectAll(\"tables\",true)' class='pointer'>"._ADMIN_SELECT_ALL."</span>\n";
		echo "&middot;\n";
		echo "<span onclick='return selectAll(\"tables\",false)' class='pointer'>"._ADMIN_DESELECT_ALL."</span>\n";
		echo "</div>\n";

		echo "<table class='no-border center'>\n";
		echo "<tr>\n";
		echo "<td width='20%' class='no-border'><label for='noprefix'>"._ADMIN_BACKUP_OPTION_NOPREFIX."</label></td>\n";
		echo "<td width='20%' class='no-border'><label for='tables'>"._ADMIN_BACKUP_OPTION_STRUCTURE."</label></td>\n";
		echo "<td width='20%' class='no-border'><label for='data'>"._ADMIN_BACKUP_OPTION_DATA."</label></td>\n";
		echo "<td width='20%' class='no-border'><label for='xhtml'>"._ADMIN_BACKUP_OPTION_XHTML."</label></td>\n";
		echo "<td width='20%' class='no-border'><label for='gzip'>"._ADMIN_BACKUP_OPTION_GZIP."</label></td>\n";
		echo "</tr>\n";
		echo "<tr>\n";
		echo "<td class='no-border'>"._ADMIN_YES." <input id='noprefix' name='noprefix' type='checkbox' value='1' /></td>\n";
		echo "<td class='no-border'>"._ADMIN_YES." <input id='structure' name='structure' type='checkbox' value='1' checked='checked' /></td>\n";
		echo "<td class='no-border'>"._ADMIN_YES." <input id='data' name='data' type='checkbox' value='1' checked='checked' /></td>\n";
		echo "<td class='no-border'>"._ADMIN_YES." <input id='xhtml' name='xhtml' type='checkbox' value='1' onclick='if(this.checked){document.getElementById(\"gzip\").checked = false;}' /></td>\n";
		echo "<td class='no-border'>"._ADMIN_YES." <input id='gzip' name='gzip' type='checkbox' value='1' onclick='if(this.checked){document.getElementById(\"xhtml\").checked = false;}' /></td>\n";
		echo "</tr>\n";
		echo "</table>\n";

		echo "<div class='center'>\n";
		echo "<input name='backup' type='submit' value='"._ADMIN_BACKUP_SUBMIT."' />\n";
		echo "</div>\n";

		echo "</fieldset>\n";
		echo "</form>\n";
    }

	/**
	 * Remove prefix from database table
	 *
	 * @param string $name
	 * @return string
	 */
	public function truncatePrefix($name)
	{
        if(_post('noprefix')) {
			return substr($name,strlen(db::prefix(NULL)));
		} else {
			return $name;
		}
	}

	/**
	 * Create backup
	 *
	 * @param boolean $xhtml
	 * @return string
	 */
	private function process($xhtml)
	{
		// log action
		Actionlog::add(_LOG_ADMIN_BACKUP_STORE);

		// start buffering
		ob_start();

		//////////// SQLite ///////////////////////////////

		if (_post('backup') && db::getType() == 'sqlite') {
			foreach (_post('tables') as $table) {
				// table name (including prefix)
				$defTable = $table;
				// handle prefix
		        $table = $this->truncatePrefix($table);
				if (_post('structure')) {
					$dbTable = db::fQuery("SELECT sql FROM sqlite_master WHERE name='".db::escape($defTable)."' LIMIT 1");
					if ($dbTable) {
						echo "\n-- TABLE: $table --\n";
						echo "DROP TABLE IF EXISTS $table;\n";

						// handle prefix in sqlite_master table
						echo preg_replace_callback('/^CREATE\s+TABLE\s+([a-zA-Z0-9_-]+)\s+\(/',create_function('$matches','$ref = new Admin_Backup; return "CREATE TABLE ".$ref->truncatePrefix($matches[1])." (";'),$dbTable);
						echo ";\n";
					}
				}
				if (_post('data')) {
					$sql    = db::query("SELECT * FROM $defTable");
					$rows   = db::numRows($sql);
					$fields = db::numFields($sql);
					if ($rows > 0) {
						echo "\n-- TABLE DATA: $table --\n";
						for ($i = 0; $i < $rows; $i++) {
							$row = db::fetchRow($sql);
							echo "INSERT INTO $table VALUES(";
							for ($j = 0; $j < $fields; $j++) {
								if ($j > 0) echo ",";
								echo "'".db::escape($row[$j])."'";
							}
							echo ");\n";
						}
					}
				}
			}
		}

		//////////// MySQL 4.0, 4.1, 5.0, ... ////////////

		else if(_post('backup') && preg_match('/^mysql(i)?$/',db::getType())) {
			foreach (_post('tables') as $table) {
				if (_post('structure')) {
					echo "\n-- TABLE: ".$this->truncatePrefix($table)." --\n";

					echo "DROP TABLE IF EXISTS ".$this->truncatePrefix($table).";\n";
					echo "CREATE TABLE ".$this->truncatePrefix($table)." (\n";

					$i   = 0;
					$sql = db::query("SHOW COLUMNS FROM $table");
					while ($columns = db::fetchArray($sql,MYSQL_ASSOC)) {
						if($i > 0) echo ",\n";

						$field = '`'.$columns['Field'].'`';
						$type  = strtoupper($columns['Type']);
						$null  = strtoupper($columns['Null']) != 'YES' ? 'NOT NULL' : NULL;

						switch ($columns['Key']) {
							case 'MUL':
							case 'UNI':
							case 'PRI':
								$key = true;
								break;
							default:
								$key = NULL;
						}

						if ($key == NULL && strlen($columns['Default']) > 0) {
							$default = "DEFAULT '".db::Escape($columns['Default'])."'";
						} else {
							$default = NULL;
						}
	
						switch (strtoupper($columns['Extra'])) {
							case 'AUTO_INCREMENT':
								$extra = 'AUTO_INCREMENT';
								break;
	
							default:
								$extra = NULL;
						}
	
						echo $field." ".$type." ".$null." ".$default." ".$extra;
	
						++$i;
					}

					$keys = db::query("SHOW KEYS FROM $table");
					while ($key = db::fetchArray($keys,MYSQL_ASSOC)) {
						echo ",\n";

						if ($key['Key_name'] == 'PRIMARY') {
							echo "PRIMARY KEY (`".$key['Column_name']."`)";
						} else if($key['Index_type'] == 'FULLTEXT') {
							echo "FULLTEXT KEY `".$key['Key_name']."`(`".$key['Column_name']."`)";
						} else if($key['Non_unique'] == 0) {
							echo "UNIQUE KEY `".$key['Key_name']."`(`".$key['Column_name']."`)";
						} else {
							echo "KEY `".$key['Key_name']."`(`".$key['Column_name']."`)";
						}
					}
					db::free($keys);

					echo "\n);\n\n";
					db::free($sql);
				}

				if (_post('data')) {
					$sql    = db::query("SELECT * FROM $table");
					$rows   = db::numRows($sql);
					$fields = db::numFields($sql);
					if ($rows > 0) {
						echo "\n-- TABLE DATA: ".$this->truncatePrefix($table)." --\n";
						echo "INSERT INTO ".$this->truncatePrefix($table)."\n";
						for ($i = 0; $i < $rows; $i++) {
							if($i > 0) echo "),\n"; else echo " VALUES";
							$row = db::fetchRow($sql);
							echo "(";
							for ($j = 0; $j < $fields; $j++) {
								if ($j > 0) echo ",";
								echo "'".db::escape($row[$j])."'";
							}
							if($i == $rows-1) echo ");\n";
						}
					}
				}
			}
		}

		$buffer = ob_get_contents();
		ob_end_clean();

		return trim($buffer);
	}
}
